package conMgr.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Observable;

/**
 * Provides an array list implementation that is observable 
 * @author is4@uw.edu
 * @param <E> The type of elements contained in the list.
 */
public class ObservableList<E> extends Observable implements List<E>
{
	/**
	 * The underlying data store.
	 */
	private final List<E> list;
	
	/**
	 * Initializes a new observable list instance.
	 */
	public ObservableList()
	{
		list = new ArrayList<E>();
	}
	
	/**
	 * Adds a new element to the list.
	 */
	@Override
	public boolean add(final E element)
	{
		final boolean result = list.add(element);
		publishUpdate();
		return result;
	}

	/**
	 * Insert a new element to the list at the specified index.
	 * @param index The index to insert the element at.
	 * @param element The element to insert.
	 */
	@Override
	public void add(final int index, final E element)
	{
		list.add(index, element);
		publishUpdate();
	}

	/**
	 * Adds a collection of new elements to this list.
	 * @param collection The collection of elements to add.
	 */
	@Override
	public boolean addAll(final Collection<? extends E> collection)
	{
		final boolean result = list.addAll(collection);
		publishUpdate();
		return result;
	}
	
	/**
	 * Insert a collection of new elements in this list at the specified index. 
	 * @param index The index to insert the element at.
	 * @param collection The collection of items to insert.
	 */
	@Override
	public boolean addAll
	(
		final int index,
		final Collection<? extends E> collection
	)
	{
		final boolean result = list.addAll(index, collection);
		publishUpdate();
		return result;
	}

	/**
	 * Clears this list of all elements.
	 */
	@Override
	public void clear()
	{
		list.clear();
		publishUpdate();
	}

	/**
	 * Determines if the list contains the given element.
	 * @param element The element to check for.
	 * @return True if the element is in the list; otherwise, false.
	 */
	@Override
	public boolean contains(final Object element)
	{
		return list.contains(element);
	}

	/**
	 * Determines if the list contains all of the given elements. 
	 * @param elements The elements to check for.
	 * @return True if all of the given elements are contained in the list;
	 * otherwise, false.
	 */
	@Override
	public boolean containsAll(final Collection<?> elements)
	{
		return list.containsAll(elements);
	}

	/**
	 * Gets the element at the given index. 
	 */
	@Override
	public E get(int index)
	{
		return list.get(index);
	}

	/**
	 * Gets the index of the given element in the list.
	 * @param element The element to get the index of.
	 * @return The 0-based index of the element in the list.
	 */
	@Override
	public int indexOf(final Object element)
	{
		return list.indexOf(element);
	}

	/**
	 * Determines if the list is empty.
	 * @return True if the list is empty; otherwise, false.
	 */
	@Override
	public boolean isEmpty()
	{
		return list.isEmpty();
	}

	/**
	 * Gets an iterator for this list.
	 * @return An iterator for the list.
	 */
	@Override
	public Iterator<E> iterator()
	{
		return list.iterator();
	}

	/**
	 * Gets the index of the last occurrence of the given element in this list.
	 * @param element The element to search for.
	 */
	@Override
	public int lastIndexOf(final Object element)
	{
		return list.lastIndexOf(element);
	}

	/**
	 * Gets a list iterator for this list. This method is unsupported in this
	 * implementation.
	 * @return A list iterator for this list.
	 */
	@Override
	public ListIterator<E> listIterator()
	{
		throw new UnsupportedOperationException("This list does not support list iterators.");
	}

	/**
	 * Gets a list iterator for this list starting at the given index. This
	 * method is unsupported in this implementation.
	 * @param index The index at which to start the iterator.
	 * @return A list iterator for this list.
	 */
	@Override
	public ListIterator<E> listIterator(final int index)
	{
		throw new UnsupportedOperationException("This list does not support list iterators.");
	}
	
	/**
	 * Publishes a change notification from this object.
	 */
	private void publishUpdate()
	{
		setChanged();
		notifyObservers();
	}

	/**
	 * Removes the given element from the list. 
	 * @param element The element to remove from the list.
	 * @return True if a matching element was found and removed; otherwise,
	 * false.
	 */
	@Override
	public boolean remove(final Object element)
	{
		final boolean result = list.remove(element);
		publishUpdate();
		return result;
	}

	/**
	 * Removes the element at the given index from the list. 
	 * @param index The index at which to remove an element.
	 * @return The removed element.
	 */
	@Override
	public E remove(final int index)
	{
		final E item = list.remove(index);
		publishUpdate();
		return item;
	}

	/**
	 * Removes all the given elements from this list.
	 * @param elements The elements to remove from the list.
	 * @return True if the list changed as a result; otherwise, false.
	 */
	@Override
	public boolean removeAll(final Collection<?> elements)
	{
		final boolean result = list.removeAll(elements);
		publishUpdate();
		return result;
	}

	/**
	 * Removes all but the given elements from this list.
	 * @param elements The elements to retain in this list.
	 * @return True if the list changed as a result; otherwise, false. 
	 */
	@Override
	public boolean retainAll(final Collection<?> elements)
	{
		final boolean result = list.retainAll(elements);
		publishUpdate();
		return result;
	}

	/**
	 * Sets the element at the given index.
	 * @param index The index to change.
	 * @param element The new element.
	 * @return The element previously at the given index. 
	 */
	@Override
	public E set(final int index, final E element)
	{
		final E item = set(index, element);
		publishUpdate();
		return item;
	}

	/**
	 * Gets the size of the list.
	 * @return The number of elements in the list.
	 */
	@Override
	public int size()
	{
		return list.size();
	}

	/**
	 * Gets a subset of the elements in this list.
	 * @param fromIndex The inclusive lower-bound of the sub list.
	 * @param toIndex The exclusive upper-bound of the sub list.
	 * @return A new sublist from the given bounds.
	 */
	@Override
	public List<E> subList(final int fromIndex, final int toIndex)
	{
		return list.subList(fromIndex, toIndex);
	}

	/**
	 * Gets a new array from this list.
	 * @return A new array containing the same elements that are in this list.
	 */
	@Override
	public Object[] toArray()
	{
		return list.toArray();
	}

	/**
	 * Gets a new array from this list.
	 * @param array The array into which to insert elements.
	 * @return An array containing the elements in this list.
	 */
	@Override
	public <T> T[] toArray(final T[] array)
	{
		return list.toArray(array);
	}
}
